package work.linruchang.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Image;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 增强的pdf工具类
 * <p>
 * 官网：https://kb.itextpdf.com/home/it7kb/examples
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2022/09/01
 * @since 1.8
 **/
@Slf4j
public class EnhanceItextPdfUtil {

    /**
     * 本地文件转成Itex的图片对象  【转换失败不抛出异常直接返回null】
     *
     * @param imageFile
     * @return
     */
    public static Image toItexImage(File imageFile) {
        return toItexImage(imageFile, 1);
    }

    /**
     * 本地文件转成Itex的图片对象  【转换失败不抛出异常直接返回null】
     *
     * @param imageFile    图片文件
     * @param imageQuality 压缩质量 0~1之前
     * @return
     */
    public static Image toItexImage(File imageFile, Number imageQuality) {
        imageQuality = ObjectUtil.defaultIfNull(imageQuality, 1);
        Assert.checkBetween(imageQuality, 0, 1);
        if (FileUtil.isFile(imageFile)) {
            try {
                ImageData imageData = null;
                if (Convert.toInt(imageQuality) != 1) {
                    byte[] compressByteArray = EnhanceImgUtil.compress(imageFile, imageQuality);
                    imageData = ImageDataFactory.create(compressByteArray);
                } else {
                    imageData = ImageDataFactory.create(imageFile.getAbsolutePath());
                }
                return new Image(imageData);
            } catch (Exception e) {
                log.error("错误：转换ITEX图片对象：{} \n {}", FileUtil.getAbsolutePath(imageFile), ExceptionUtil.stacktraceToOneLineString(e));
                return null;
            }
        }
        return null;
    }

    /**
     * java图片对象转成Itex图片对象
     *
     * @param awtImage
     * @return
     */
    public static Image toItexImage(java.awt.Image awtImage) {
        if (awtImage != null) {
            try {
                ImageData imageData = ImageDataFactory.create(awtImage, null);
                return new Image(imageData);
            } catch (Exception e) {
                log.error("错误：转换ITEX图片对象：  {}", ExceptionUtil.stacktraceToOneLineString(e));
                return null;
            }
        }
        return null;
    }


    /**
     * 自定义新的宽高PDF页面，如果width、height有任一一个入参含null，都会使用默认A4页面
     *
     * @param width  宽
     * @param height 高
     * @return
     */
    public static AreaBreak customNewPage(Number width, Number height) {
        PageSize pageSize = ObjectUtil.hasNull(width, height) ? PageSize.DEFAULT : new PageSize(Convert.toFloat(width), Convert.toFloat(height));
        return new AreaBreak(pageSize);
    }

    /**
     * 新的pdf页面根据图片的宽高来定义
     *
     * @param image 图片
     * @return
     */
    public static AreaBreak customNewPageByImage(Image image) {
        return customNewPage(image.getImageWidth(), image.getImageHeight());
    }


    /**
     * 图片转PDF
     *
     * @param imageFiles        图片文件
     * @param targetPdfFilePath 合并后的pdf文件
     * @return
     */
    public static File imagesToPdf(List<File> imageFiles, String targetPdfFilePath) {
        return imagesToPdf(imageFiles, null, null, 1, FileUtil.file(targetPdfFilePath));
    }

    /**
     * 图片转PDF
     *
     * @param imageFiles        图片文件
     * @param targetPdfFilePath 合并后的pdf文件
     * @return
     */
    public static File imagesToPdf(List<File> imageFiles, File targetPdfFilePath) {
        return imagesToPdf(imageFiles, null, null, 1, targetPdfFilePath);
    }

    /**
     * 图片转PDF（每张图就是每页的PDF（每张根据图片的宽高进行调整））
     * 文档：https://kb.itextpdf.com/home/it7kb/faq/how-to-create-a-document-with-unequal-page-sizes
     *
     * @param imageFilter       文件过滤
     * @param imageFiles        一系列图片文件
     * @param sortComparator    【imageFiles】如何排序拼接成pdf == null则按默认排序
     * @param imageQuality      图片压缩
     * @param targetPdfFilePath 合并后的pdf路径
     */
    @SneakyThrows
    public static File imagesToPdf(List<File> imageFiles, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, File targetPdfFilePath) {
        imageFiles = CollUtil.emptyIfNull(imageFiles).stream()
                .filter(EnhanceImgUtil::isImage)
                .filter(imageFile -> imageFilter != null ? imageFilter.accept(imageFile) : true)
                .collect(Collectors.toList());

        // 排序
        if (sortComparator != null) {
            CollUtil.sort(imageFiles, sortComparator);
        }

        if (CollUtil.isNotEmpty(imageFiles) && targetPdfFilePath != null) {
            // 创建PDF实例
            PdfWriter pdfWriter = new PdfWriter(targetPdfFilePath);
            // 管理跟踪、添加的内容
            PdfDocument pdfDocument = new PdfDocument(pdfWriter);

            File firstImageFile = CollUtil.getFirst(imageFiles);
            Image firstImage = toItexImage(firstImageFile, imageQuality);
            Document document = new Document(pdfDocument, customNewPageByImage(firstImage).getPageSize());
            document.add(firstImage);

            List<File> removeFirstImageList = CollUtil.removeAny(imageFiles, firstImageFile);
            removeFirstImageList.stream()
                    .map(imageFile -> EnhanceItextPdfUtil.toItexImage(imageFile, ObjectUtil.defaultIfNull(imageQuality,1)))
                    .forEachOrdered(image -> {
                        document.add(customNewPageByImage(image));
                        document.setMargins(10, 10, 10, 10);
                        document.add(image);
                    });

            document.close();
            return targetPdfFilePath;
        }
        return null;
    }


    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param imageQuality      图片压缩 0~1
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, File targetPdfFilePath) {
        if (!FileUtil.isDirectory(sourceImagesDir)) {
            log.warn("sourceImagesDir文件非目录：{}", FileUtil.getAbsolutePath(sourceImagesDir));
            return null;
        }

        // 仅需图片文件
        List<File> imageFiles = FileUtil.loopFiles(sourceImagesDir, file -> {
            return StrUtil.equalsAnyIgnoreCase(FileUtil.extName(file), EnhanceImgUtil.IMAGE_TYPE_JPG, EnhanceImgUtil.IMAGE_TYPE_GIF, EnhanceImgUtil.IMAGE_TYPE_JPEG, EnhanceImgUtil.IMAGE_TYPE_BMP, EnhanceImgUtil.IMAGE_TYPE_PNG, EnhanceImgUtil.IMAGE_TYPE_PSD);
        });
        return imagesToPdf(imageFiles, imageFilter, sortComparator, imageQuality, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param imageQuality      图片压缩 0~1
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, Number imageQuality, String targetPdfFilePath) {
        return imagesToPdfByDir(FileUtil.file(sourceImagesDir), imageFilter, sortComparator, imageQuality, FileUtil.file(targetPdfFilePath));
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, imageFilter, sortComparator, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param imageFilter       需要合并的图片
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, FileFilter imageFilter, Comparator<File> sortComparator, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, imageFilter, sortComparator, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, Comparator<File> sortComparator, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, sortComparator, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param sortComparator    图片排序
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, Comparator<File> sortComparator, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, sortComparator, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(File sourceImagesDir, File targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, null, targetPdfFilePath);
    }

    /**
     * 将目录中的图片合成PDF
     *
     * @param sourceImagesDir   图片目录
     * @param targetPdfFilePath 合并后的pdf
     * @return
     */
    public static File imagesToPdfByDir(String sourceImagesDir, String targetPdfFilePath) {
        return imagesToPdfByDir(sourceImagesDir, null, null, targetPdfFilePath);
    }


    /**
     * 图片转PDF
     *
     * @param sourceFileStr 单张图片文件或者存储图片文件的目录
     * @param targetFileStr 将上述sourceFileStr文件转成pdf后，pdf文件的路径
     */
    @SneakyThrows
    private static void imagesToPdfBySourFileType(String sourceFileStr, String targetFileStr) {
        if (StrUtil.isAllNotBlank(sourceFileStr, targetFileStr) && FileUtil.exist(sourceFileStr)) {
            if (FileUtil.isDirectory(sourceFileStr)) {
                imagesToPdfByDir(sourceFileStr, targetFileStr);
            } else {
                imagesToPdf(Arrays.asList(FileUtil.file(sourceFileStr)), targetFileStr);
            }
        }
    }
}
